package com.gitee.wsl.lang

import com.gitee.wsl.ext.base.Arrays.arraycopy

class IntArrayStack {
    class UnderflowException(message: String?) : RuntimeException(message)

    private var array: IntArray
    private var top: Int

    init {
        array = IntArray(INITIAL_CAPACITY)
        top = -1
    }

    val isEmpty: Boolean
        /**
         * Tests if the stack is empty.
         *
         * @return true if empty, false otherwise.
         */
        get() = top == -1

    /**
     * Returns the number of element currently on the stack.
     *
     * @return the number of element currently on the stack
     */
    fun size(): Int {
        return top + 1
    }

    /**
     * Copies all elements currently on the stack into the given array.
     *
     * @param destArray the array
     * @param destStartIndex the index to start copying into
     */
    fun getElements(destArray: IntArray, destStartIndex: Int) {
        arraycopy(array, 0, destArray, destStartIndex, size())
    }

    /**
     * @return all elements in a new array.
     */
    fun toArray(): IntArray {
        val array = IntArray(size())
        getElements(array, 0)
        return array
    }

    /**
     * Empties the stack.
     */
    fun clear() {
        top = -1
    }

    /**
     * Returns the item at the top of the stack without removing it.
     *
     * @return the most recently inserted item in the stack.
     * @throws UnderflowException if the stack is empty.
     */
    fun peek(): Int {
        if (isEmpty) {
            throw UnderflowException("IntArrayStack peek")
        }
        return array[top]
    }

    /**
     * Removes the most recently inserted item from the stack.
     *
     * @return the top stack item
     * @throws UnderflowException if the stack is empty.
     */
    fun pop(): Int {
        if (isEmpty) {
            throw UnderflowException("IntArrayStack pop")
        }
        return array[top--]
    }

    /**
     * Pushes a new item onto the stack.
     *
     * @param x the item to add.
     */
    fun push(x: Int) {
        if (top == array.size - 1) {
            expandCapacity()
        }
        array[++top] = x
    }

    private fun expandCapacity() {
        val newArray = IntArray(array.size * 2)
        arraycopy(array, 0, newArray, 0, array.size)
        array = newArray
    }

    companion object {
        private const val INITIAL_CAPACITY = 16
    }
}
